﻿Imports System
Imports System.ComponentModel
Imports System.ComponentModel.Design
Imports System.Drawing
Imports System.Runtime.InteropServices
Imports System.Workflow.ComponentModel
Imports System.Workflow.ComponentModel.Compiler
Imports System.Workflow.ComponentModel.Design

Namespace System.Workflow.Activities
    <SRDescription("StateActivityDescription"), ToolboxItem(GetType(ActivityToolboxItem)), Designer(GetType(StateDesigner), GetType(IDesigner)), ComVisible(False), SRCategory("Standard"), ToolboxBitmap(GetType(StateActivity), "Resources.StateActivity.png"), ActivityValidator(GetType(StateActivityValidator))> _
    Public Class StateActivity
        Inherits CompositeActivity
        ' Methods
        Public Sub New()
        End Sub

        Public Sub New(ByVal name As String)
            MyBase.New(name)
        End Sub

        Protected Overrides Function Cancel(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            StateActivity.CleanUp(executionContext)
            Dim flag As Boolean = True
            Dim context As ActivityExecutionContext
            For Each context In executionContext.ExecutionContextManager.ExecutionContexts
                If (context.Activity.Parent Is Me) Then
                    flag = False
                    If (context.Activity.ExecutionStatus = ActivityExecutionStatus.Executing) Then
                        context.CancelActivity(context.Activity)
                    End If
                End If
            Next
            If Not flag Then
                Return MyBase.ExecutionStatus
            End If
            Return ActivityExecutionStatus.Closed
        End Function

        Private Shared Sub CleanUp(ByVal context As ActivityExecutionContext)
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            Dim state As StateActivity = DirectCast(context.Activity, StateActivity)
            If (state.ExecutionStatus <> ActivityExecutionStatus.Faulting) Then
                Dim subscriptionManager As StateMachineSubscriptionManager = StateActivity.GetExecutionState(state).SubscriptionManager
                subscriptionManager.UnsubscribeState(context)
                If StateMachineHelpers.IsRootState(state) Then
                    subscriptionManager.DeleteSetStateEventQueue(context)
                ElseIf StateMachineHelpers.IsLeafState(state) Then
                    subscriptionManager.UnsubscribeToSetStateEvent(context)
                End If
            End If
        End Sub

        Private Shared Sub CleanupChildAtClosure(ByVal context As ActivityExecutionContext, ByVal childActivity As Activity)
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            If (childActivity Is Nothing) Then
                Throw New ArgumentNullException("childActivity")
            End If
            Dim state As StateActivity = DirectCast(context.Activity, StateActivity)
            StateActivity.GetExecutionState(state)
            RemoveHandler childActivity.Closed, New EventHandler([Of] ActivityExecutionStatusChangedEventArgs)(AddressOf state.HandleChildActivityClosed)
            Dim executionContextManager As ActivityExecutionContextManager = context.ExecutionContextManager
            Dim executionContext As ActivityExecutionContext = executionContextManager.GetExecutionContext(childActivity)
            executionContextManager.CompleteExecutionContext(executionContext)
        End Sub

        Private Shared Sub Complete(ByVal context As ActivityExecutionContext)
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            Dim activity As StateActivity = DirectCast(context.Activity, StateActivity)
            Dim executionState As StateMachineExecutionState = StateActivity.GetExecutionState(activity)
            If StateMachineHelpers.IsLeafState(activity) Then
                executionState.PreviousStateName = activity.Name
            End If
            StateActivity.CleanUp(context)
            executionState.SchedulerBusy = True
            context.CloseActivity
        End Sub

        Private Shared Sub EnteringLeafState(ByVal context As ActivityExecutionContext)
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            Dim activity As StateActivity = DirectCast(context.Activity, StateActivity)
            Dim executionState As StateMachineExecutionState = StateActivity.GetExecutionState(activity)
            executionState.SubscriptionManager.SubscribeToSetStateEvent(context)
            StateMachineHelpers.GetCompletedStateName(activity)
            If StateMachineHelpers.IsCompletedState(activity) Then
                Dim action As New EnteringStateAction(activity.QualifiedName)
                executionState.EnqueueAction(action)
                executionState.ProcessActions(context)
                executionState.Completed = True
                StateActivity.LeavingState(context)
            Else
                If String.IsNullOrEmpty(executionState.NextStateName) Then
                    executionState.SubscriptionManager.ReevaluateSubscriptions(context)
                    Dim action2 As New EnteringStateAction(activity.QualifiedName)
                    executionState.EnqueueAction(action2)
                    executionState.LockQueue
                Else
                    Dim action3 As New EnteringStateAction(activity.QualifiedName)
                    executionState.EnqueueAction(action3)
                    executionState.ProcessTransitionRequest(context)
                End If
                executionState.ProcessActions(context)
            End If
        End Sub

        Protected Overrides Function Execute(ByVal executionContext As ActivityExecutionContext) As ActivityExecutionStatus
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If StateMachineHelpers.IsRootState(Me) Then
                Me.ExecuteRootState(executionContext)
            ElseIf StateMachineHelpers.IsLeafState(Me) Then
                StateActivity.ExecuteLeafState(executionContext)
            Else
                StateActivity.ExecuteState(executionContext)
            End If
            Return MyBase.ExecutionStatus
        End Function

        Private Shared Sub ExecuteChild(ByVal context As ActivityExecutionContext, ByVal childActivity As Activity)
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            If (childActivity Is Nothing) Then
                Throw New ArgumentNullException("childActivity")
            End If
            Dim state As StateActivity = DirectCast(context.Activity, StateActivity)
            StateActivity.GetExecutionState(state).SchedulerBusy = True
            Dim context2 As ActivityExecutionContext = context.ExecutionContextManager.CreateExecutionContext(childActivity)
            AddHandler context2.Activity.Closed, New EventHandler([Of] ActivityExecutionStatusChangedEventArgs)(AddressOf state.HandleChildActivityClosed)
            context2.ExecuteActivity(context2.Activity)
        End Sub

        Friend Shared Sub ExecuteEventDriven(ByVal context As ActivityExecutionContext, ByVal eventDriven As EventDrivenActivity)
            StateActivity.GetExecutionState(context)
            StateActivity.ExecuteChild(context, eventDriven)
        End Sub

        Private Shared Sub ExecuteLeafState(ByVal context As ActivityExecutionContext)
            Dim activity As StateActivity = DirectCast(context.Activity, StateActivity)
            Dim executionState As StateMachineExecutionState = StateActivity.GetExecutionState(activity)
            executionState.SchedulerBusy = False
            executionState.CurrentStateName = activity.QualifiedName
            Dim stateInitialization As StateInitializationActivity = StateActivity.GetStateInitialization(context)
            If (Not stateInitialization Is Nothing) Then
                StateActivity.ExecuteStateInitialization(context, stateInitialization)
            Else
                StateActivity.EnteringLeafState(context)
            End If
        End Sub

        Private Sub ExecuteRootState(ByVal context As ActivityExecutionContext)
            Dim activity As StateActivity = DirectCast(context.Activity, StateActivity)
            Dim state As New StateMachineExecutionState(MyBase.WorkflowInstanceId) { _
                .SchedulerBusy = False _
            }
            activity.SetValue(StateActivity.StateMachineExecutionStateProperty, state)
            state.SubscriptionManager.CreateSetStateEventQueue(context)
            Dim initialStateName As String = StateMachineHelpers.GetInitialStateName(activity)
            state.CalculateStateTransition(Me, initialStateName)
            state.ProcessActions(context)
        End Sub

        Private Shared Sub ExecuteState(ByVal context As ActivityExecutionContext)
            Dim executionState As StateMachineExecutionState = StateActivity.GetExecutionState(context)
            executionState.SchedulerBusy = False
            executionState.ProcessActions(context)
        End Sub

        Friend Shared Sub ExecuteState(ByVal context As ActivityExecutionContext, ByVal state As StateActivity)
            StateActivity.GetExecutionState(context)
            StateActivity.ExecuteChild(context, state)
        End Sub

        Private Shared Sub ExecuteStateFinalization(ByVal context As ActivityExecutionContext, ByVal stateFinalization As StateFinalizationActivity)
            StateActivity.GetExecutionState(context)
            StateActivity.ExecuteChild(context, stateFinalization)
        End Sub

        Private Shared Sub ExecuteStateInitialization(ByVal context As ActivityExecutionContext, ByVal stateInitialization As StateInitializationActivity)
            StateActivity.GetExecutionState(context)
            StateActivity.ExecuteChild(context, stateInitialization)
        End Sub

        Public Function GetDynamicActivity(ByVal childActivityName As String) As Activity
            If (childActivityName Is Nothing) Then
                Throw New ArgumentNullException("childActivityName")
            End If
            Dim childActivity As Activity = Nothing
            Dim i As Integer
            For i = 0 To MyBase.EnabledActivities.Count - 1
                If MyBase.EnabledActivities.Item(i).QualifiedName.Equals(childActivityName) Then
                    childActivity = MyBase.EnabledActivities.Item(i)
                    Exit For
                End If
            Next i
            If (childActivity Is Nothing) Then
                Throw New ArgumentException(SR.GetString("Error_StateChildNotFound"), "childActivityName")
            End If
            Return Me.GetDynamicActivity(childActivity)
        End Function

        <DesignerSerializationVisibility(DesignerSerializationVisibility.Hidden), Browsable(False)> _
        Private Function GetDynamicActivity(ByVal childActivity As Activity) As Activity
            If (childActivity Is Nothing) Then
                Throw New ArgumentNullException("childActivity")
            End If
            If Not MyBase.EnabledActivities.Contains(childActivity) Then
                Throw New ArgumentException(SR.GetString("Error_StateChildNotFound"), "childActivity")
            End If
            Dim dynamicActivities As Activity() = MyBase.GetDynamicActivities(childActivity)
            If (dynamicActivities.Length <> 0) Then
                Return dynamicActivities(0)
            End If
            Return Nothing
        End Function

        Private Shared Function GetExecutionState(ByVal state As StateActivity) As StateMachineExecutionState
            If (state Is Nothing) Then
                Throw New ArgumentNullException("state")
            End If
            Return StateMachineExecutionState.Get(StateMachineHelpers.GetRootState(state))
        End Function

        Private Shared Function GetExecutionState(ByVal context As ActivityExecutionContext) As StateMachineExecutionState
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            Dim state As StateActivity = DirectCast(context.Activity, StateActivity)
            Return StateActivity.GetExecutionState(state)
        End Function

        Private Shared Function GetHandlerActivity([Of] T As Class)(ByVal context As ActivityExecutionContext) As T
            Dim activity As StateActivity = DirectCast(context.Activity, StateActivity)
            Dim activity2 As Activity
            For Each activity2 In activity.EnabledActivities
                Dim local As T = [TryCast](activity2,T)
                If (Not local Is Nothing) Then
                    Return local
                End If
            Next
            Return CType(Nothing, T)
        End Function

        Private Shared Function GetStateFinalization(ByVal context As ActivityExecutionContext) As StateFinalizationActivity
            Dim activity As StateActivity = DirectCast(context.Activity, StateActivity)
            Return StateActivity.GetHandlerActivity([Of] StateFinalizationActivity)(context)
        End Function

        Private Shared Function GetStateInitialization(ByVal context As ActivityExecutionContext) As StateInitializationActivity
            Dim activity As StateActivity = DirectCast(context.Activity, StateActivity)
            Return StateActivity.GetHandlerActivity([Of] StateInitializationActivity)(context)
        End Function

        Private Sub HandleChildActivityClosed(ByVal sender As Object, ByVal eventArgs As ActivityExecutionStatusChangedEventArgs)
            Dim context As ActivityExecutionContext = [TryCast](sender,ActivityExecutionContext)
            If (context Is Nothing) Then
                Throw New ArgumentException(SR.Error_SenderMustBeActivityExecutionContext, "sender")
            End If
            If (eventArgs Is Nothing) Then
                Throw New ArgumentNullException("eventArgs")
            End If
            Dim childActivity As Activity = eventArgs.Activity
            Dim activity As StateActivity = DirectCast(context.Activity, StateActivity)
            StateActivity.GetExecutionState(context).SchedulerBusy = False
            StateActivity.CleanupChildAtClosure(context, childActivity)
            Select Case activity.ExecutionStatus
                Case ActivityExecutionStatus.Executing
                    If Not TypeOf childActivity Is EventDrivenActivity Then
                        Dim stateInitialization As StateInitializationActivity = [TryCast](childActivity,StateInitializationActivity)
                        If (Not stateInitialization Is Nothing) Then
                            StateActivity.HandleStateInitializationCompleted(context, stateInitialization)
                            Return
                        End If
                        If TypeOf childActivity Is StateFinalizationActivity Then
                            StateActivity.HandleStateFinalizationCompleted(context)
                            Return
                        End If
                        If TypeOf childActivity Is StateActivity Then
                            StateActivity.HandleSubStateCompleted(context)
                            Return
                        End If
                        StateActivity.InvalidChildActivity(activity)
                        Return
                    End If
                    StateActivity.HandleEventDrivenCompleted(context)
                    Return
                Case ActivityExecutionStatus.Canceling, ActivityExecutionStatus.Faulting
                    context.CloseActivity
                    Return
            End Select
            Throw New InvalidOperationException(SR.GetInvalidActivityStatus(context.Activity))
        End Sub

        Private Shared Sub HandleEventDrivenCompleted(ByVal context As ActivityExecutionContext)
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            Dim activity As StateActivity = DirectCast(context.Activity, StateActivity)
            Dim executionState As StateMachineExecutionState = StateActivity.GetExecutionState(context)
            If String.IsNullOrEmpty(executionState.NextStateName) Then
                executionState.SubscriptionManager.ReevaluateSubscriptions(context)
                executionState.LockQueue
            Else
                executionState.ProcessTransitionRequest(context)
            End If
            executionState.ProcessActions(context)
        End Sub

        Private Sub HandleProcessActionEvent(ByVal sender As Object, ByVal eventArgs As EventArgs)
            Dim context As ActivityExecutionContext = [TryCast](sender,ActivityExecutionContext)
            If (context Is Nothing) Then
                Throw New ArgumentException(SR.Error_SenderMustBeActivityExecutionContext, "sender")
            End If
            Dim executionState As StateMachineExecutionState = StateActivity.GetExecutionState(context)
            executionState.SchedulerBusy = False
            executionState.ProcessActions(context)
        End Sub

        Private Shared Sub HandleStateFinalizationCompleted(ByVal context As ActivityExecutionContext)
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            StateActivity.GetExecutionState(context)
            StateActivity.Complete(context)
        End Sub

        Private Shared Sub HandleStateInitializationCompleted(ByVal context As ActivityExecutionContext, ByVal stateInitialization As StateInitializationActivity)
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            If (stateInitialization Is Nothing) Then
                Throw New ArgumentNullException("stateInitialization")
            End If
            Dim activity As StateActivity = DirectCast(context.Activity, StateActivity)
            Dim executionState As StateMachineExecutionState = StateActivity.GetExecutionState(activity)
            If (Not String.IsNullOrEmpty(executionState.NextStateName) AndAlso executionState.NextStateName.Equals(activity.QualifiedName)) Then
                Throw New InvalidOperationException(SR.GetInvalidSetStateInStateInitialization)
            End If
            StateActivity.EnteringLeafState(context)
        End Sub

        Private Shared Sub HandleSubStateCompleted(ByVal context As ActivityExecutionContext)
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            Dim executionState As StateMachineExecutionState = StateActivity.GetExecutionState(context)
            If executionState.Completed Then
                StateActivity.LeavingState(context)
            Else
                executionState.ProcessActions(context)
            End If
        End Sub

        Protected Overrides Sub Initialize(ByVal provider As IServiceProvider)
            MyBase.Initialize(provider)
            Dim context As ActivityExecutionContext = DirectCast(provider, ActivityExecutionContext)
            If Not StateMachineHelpers.IsStateMachine(StateMachineHelpers.GetRootState(Me)) Then
                Throw New InvalidOperationException(SR.GetError_StateActivityMustBeContainedInAStateMachine)
            End If
            Dim initialStateName As String = StateMachineHelpers.GetInitialStateName(Me)
            If String.IsNullOrEmpty(initialStateName) Then
                Throw New InvalidOperationException(SR.GetError_CannotExecuteStateMachineWithoutInitialState)
            End If
            If (MyBase.QualifiedName <> initialStateName) Then
                StateMachineSubscriptionManager.DisableStateWorkflowQueues(context, Me)
            End If
        End Sub

        Private Shared Sub InvalidChildActivity(ByVal state As StateActivity)
            If StateMachineHelpers.IsLeafState(state) Then
                Throw New InvalidOperationException(SR.GetError_InvalidLeafStateChild)
            End If
            Throw New InvalidOperationException(SR.GetError_InvalidCompositeStateChild)
        End Sub

        Friend Shared Sub LeavingState(ByVal context As ActivityExecutionContext)
            If (context Is Nothing) Then
                Throw New ArgumentNullException("context")
            End If
            Dim state As StateActivity = DirectCast(context.Activity, StateActivity)
            If StateMachineHelpers.IsLeafState(state) Then
                Dim stateFinalization As StateFinalizationActivity = StateActivity.GetStateFinalization(context)
                If (stateFinalization Is Nothing) Then
                    StateActivity.Complete(context)
                Else
                    StateActivity.ExecuteStateFinalization(context, stateFinalization)
                End If
            Else
                StateActivity.Complete(context)
            End If
        End Sub

        Protected Overrides Sub OnActivityChangeAdd(ByVal executionContext As ActivityExecutionContext, ByVal addedActivity As Activity)
            If (executionContext Is Nothing) Then
                Throw New ArgumentNullException("executionContext")
            End If
            If (addedActivity Is Nothing) Then
                Throw New ArgumentNullException("addedActivity")
            End If
            If (addedActivity.Enabled AndAlso (executionContext.Activity.ExecutionStatus = ActivityExecutionStatus.Executing)) Then
                Dim eventDriven As EventDrivenActivity = [TryCast](addedActivity,EventDrivenActivity)
                If (Not eventDriven Is Nothing) Then
                    StateMachineSubscriptionManager.ChangeEventDrivenQueueState(executionContext, eventDriven, False)
                    Dim state As StateMachineExecutionState = StateMachineExecutionState.Get(StateMachineHelpers.GetRootState([TryCast](executionContext.Activity,StateActivity)))
                    If (Not StateMachineHelpers.GetCurrentState(executionContext) Is Nothing) Then
                        state.SubscriptionManager.ReevaluateSubscriptions(executionContext)
                        state.LockQueue
                        state.ProcessActions(executionContext)
                    End If
                End If
            End If
        End Sub

        Protected Overrides Sub OnClosed(ByVal provider As IServiceProvider)
            MyBase.RemoveProperty(StateActivity.StateMachineExecutionStateProperty)
        End Sub

        Friend Sub RaiseProcessActionEvent(ByVal context As ActivityExecutionContext)
            StateActivity.GetExecutionState(context).SchedulerBusy = True
            MyBase.Invoke([Of] EventArgs)(New EventHandler([Of] EventArgs)(AddressOf Me.HandleProcessActionEvent), New EventArgs)
        End Sub


        ' Fields
        Public Const StateChangeTrackingDataKey As String = "StateActivity.StateChange"
        Friend Shared StateMachineExecutionStateProperty As DependencyProperty = DependencyProperty.Register("StateMachineExecutionState", GetType(StateMachineExecutionState), GetType(StateActivity), New PropertyMetadata)
    End Class
End Namespace

